﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace AMVInfoSpace
{
    public enum SITE { AMVNews, Akross, CreaCon }

    public static class AmvHtmlParser
    {
        public static string fPathAkrossTagReplList = "";

        public static Dictionary<string, string> ReplTagGanreForCreaCon = new Dictionary<string, string>()
        {
            {"ЭКШН", "Action"},
            {"МУЛЬТФИЛЬМ", "MultFilm"},
            {"ДРАМА", "Drama"},
            {"КОМЕДИЯ", "Comedy (Fun)"},
            {"ИГРА", "GMV"},
            {"ПРОФИЛЬ ПЕРСОНАЖА", "Character Profile"},
            {"ИСТОРИЯ", "Story"},
            {"ТВ СЕРИАЛ", "TV Series"},
            {"ХОРРОР", "Horror"},
            {"РОМАНТИКА", "Romance"},
            {"ТРЕЙЛЕР", "Trailer"},
            {"АВТОРСКОЕ ВИДЕО", "Original Animation"},
            {"ПСИХОДЕЛИКА", "Psychedelic"},
            {"ДЭНС", "Dance"}
        };

        public class AkrossTagReplList
        {
            /// <summary>Лист замены как {ключевое слово, список тегов, которое оно подразумевает}</summary>
            public Dictionary<string, List<string>> RepList = new Dictionary<string, List<string>>();

            public AkrossTagReplList()
            {
                if (fPathAkrossTagReplList == "") throw new Exception("Перед парсингом необходимо указать путь к файлу замены тегов для Akross (в формате csv и кодировке UTF8)");
                if (!File.Exists(fPathAkrossTagReplList)) throw new Exception(string.Format("Указанного файла замены тегов для Akross '{0}' не существует", fPathAkrossTagReplList));
                try
                {
                    string[] strl = File.ReadAllLines(fPathAkrossTagReplList, Encoding.UTF8);
                    foreach (string str in strl)
                    {
                        if (str.Trim() == "") continue;
                        string[] sl = str.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
                        string KEY = sl[0].Trim().ToUpper();
                        if (!RepList.ContainsKey(KEY)) RepList.Add(KEY, new List<string>());
                        string[] tl = sl[1].Trim().Split('|');
                        foreach (string tag in tl) RepList[KEY].Add(tag.Trim());
                    }
                }
                catch (Exception ex)
                {
                    throw new Exception(string.Format("Неверный формат файла замены тегов для Akross '{0}' или ошибка чтения", fPathAkrossTagReplList), ex);
                }
            }
        }
        public static AkrossTagReplList akrossTagReplList = null;

        /// <summary>Страничка AMVNews - это корректная страница AMV</summary>
        public static bool PageAMVNews_IsCorrect(ref string Html)
        {
            return !Html.Contains("<title>AMV | Клипы</title>");
        }

        /// <summary>Страничка Akross - это корректная страница AMV</summary>
        public static bool PageAkross_IsCorrect(ref string Html)
        {
            if (Html.Contains("<title>AKROSS » Ошибка » </title>")) return false;
            if (Html.Contains("<title>503 Service Temporarily Unavailable</title>")) return false;
            return true;
        }

        public static bool PageCreaCon_IsCorrect(ref string Html, int Uin)
        {
            if (Html.Contains("<title> :: CreativeSpace</title>")) return false;
            return !Html.Contains("<b>Ошибка!</b><ul><li>Клип (" + Uin.ToString() + ") запрещен к показу</ul></td>");
        }

        #region *** Load/Save AmvInfo List ********************************************************************

        public static List<AMVInfo> LoadAmvInfoList(string FilePath)
        {
            List<AMVInfo> ret = new List<AMVInfo>();
            if (!File.Exists(FilePath)) return ret;

            using (MemoryStream ms = new MemoryStream(File.ReadAllBytes(FilePath)))
            using (BinaryReader br = new BinaryReader(ms))
            {
                int loadVertion = br.ReadInt32();
                switch (loadVertion)
                {
                    case 0: ret = LoadAmvInfoList_0(FilePath); break;
                    case 1: ret = LoadAmvInfoList_1(br); break;
                    case 2: ret = LoadAmvInfoList_2(br); break;
                    case 3: ret = LoadAmvInfoList_3(br); break;
                }
            }
            return ret;
        }

        static List<AMVInfo> LoadAmvInfoList_0(string FilePath)
        {
            ESpace.EStream st = new ESpace.EStream(FilePath);
            st.OpenLoad();
            st.LoadInt();

            List<AMVInfo> ret = new List<AMVInfo>();
            int kolvo = st.LoadInt();
            for (int i = 0; i < kolvo; i++)
            {
                AMVInfo addAmv = new AMVInfo(st.LoadInt(), st.LoadStr(), (SITE)st.LoadInt());
                addAmv.Rating = 0;
                int kolvo1 = st.LoadInt();
                for (int j = 0; j < kolvo1; j++)
                    addAmv.Msc.Add(st.LoadStr());

                kolvo1 = st.LoadInt();
                for (int j = 0; j < kolvo1; j++)
                    addAmv.Anm.Add(st.LoadStr());

                kolvo1 = st.LoadInt();
                for (int j = 0; j < kolvo1; j++)
                    addAmv.Atr.Add(st.LoadStr());

                kolvo1 = st.LoadInt();
                for (int j = 0; j < kolvo1; j++)
                    addAmv.TagOther.Add(st.LoadStr());

                kolvo1 = st.LoadInt();
                for (int j = 0; j < kolvo1; j++)
                    addAmv.Awd.Add(st.LoadStr());

                addAmv.Comment = st.LoadStr();
                addAmv.SetFindInfo();

                ret.Add(addAmv);
            }
            st.Close();
            return ret;
        }

        static List<AMVInfo> LoadAmvInfoList_1(BinaryReader br)
        {
            List<AMVInfo> ret = new List<AMVInfo>();
            int kolvo = br.ReadInt32();
            for (int i = 0; i < kolvo; i++)
            {
                AMVInfo addAmv = new AMVInfo(br.ReadInt32(), br.ReadString(), (SITE)br.ReadInt32());
                addAmv.Rating = br.ReadDecimal();

                int kolvo1 = br.ReadInt32();
                for (int j = 0; j < kolvo1; j++)
                    addAmv.Msc.Add(br.ReadString());

                kolvo1 = br.ReadInt32();
                for (int j = 0; j < kolvo1; j++)
                    addAmv.Anm.Add(br.ReadString());

                kolvo1 = br.ReadInt32();
                for (int j = 0; j < kolvo1; j++)
                    addAmv.Atr.Add(br.ReadString());

                kolvo1 = br.ReadInt32();
                for (int j = 0; j < kolvo1; j++)
                    addAmv.TagOther.Add(br.ReadString());

                kolvo1 = br.ReadInt32();
                for (int j = 0; j < kolvo1; j++)
                    addAmv.Awd.Add(br.ReadString());

                addAmv.Comment = br.ReadString();
                addAmv.CreateDate = new DateTime(br.ReadInt64());
                addAmv.AppenedDate = new DateTime(br.ReadInt64());
                addAmv.SetFindInfo();

                ret.Add(addAmv);
            }
            return ret;
        }

        static List<AMVInfo> LoadAmvInfoList_2(BinaryReader br)
        {
            List<AMVInfo> ret = new List<AMVInfo>();
            int kolvo = br.ReadInt32();
            for (int i = 0; i < kolvo; i++)
            {
                AMVInfo addAmv = new AMVInfo(br.ReadInt32(), br.ReadString(), (SITE)br.ReadInt32());
                addAmv.Rating = br.ReadDecimal();

                int kolvo1 = br.ReadInt32();
                for (int j = 0; j < kolvo1; j++)
                    addAmv.Msc.Add(br.ReadString());

                kolvo1 = br.ReadInt32();
                for (int j = 0; j < kolvo1; j++)
                    addAmv.Anm.Add(br.ReadString());

                kolvo1 = br.ReadInt32();
                for (int j = 0; j < kolvo1; j++)
                    addAmv.Atr.Add(br.ReadString());

                kolvo1 = br.ReadInt32();
                for (int j = 0; j < kolvo1; j++)
                    addAmv.TagGenre.Add(br.ReadString());

                kolvo1 = br.ReadInt32();
                for (int j = 0; j < kolvo1; j++)
                    addAmv.TagOther.Add(br.ReadString());

                kolvo1 = br.ReadInt32();
                for (int j = 0; j < kolvo1; j++)
                    addAmv.Awd.Add(br.ReadString());

                addAmv.Comment = br.ReadString();
                addAmv.CreateDate = new DateTime(br.ReadInt64());
                addAmv.AppenedDate = new DateTime(br.ReadInt64());
                addAmv.SetFindInfo();

                ret.Add(addAmv);
            }
            return ret;
        }

        static List<AMVInfo> LoadAmvInfoList_3(BinaryReader br)
        {
            List<AMVInfo> ret = new List<AMVInfo>();
            int kolvo = br.ReadInt32();
            for (int i = 0; i < kolvo; i++)
            {
                AMVInfo addAmv = new AMVInfo(br);
                addAmv.SetFindInfo();
                ret.Add(addAmv);
            }
            return ret;
        }

        public static void SaveAmvInfoList(string FilePath, List<AMVInfo> InfoL)
        {
            using (MemoryStream ms = new MemoryStream())
            using (BinaryWriter bw = new BinaryWriter(ms))
            {
                // версия
                bw.Write(3);
                bw.Write(InfoL.Count);
                foreach (AMVInfo aInf in InfoL) aInf.Save(bw);
                ms.Flush();
                File.WriteAllBytes(FilePath, ms.ToArray());
            }
        }

        #endregion *** Load/Save AmvInfo List ********************************************************************

        #region *** Parse AmvInfo ********************************************************************

        public static AMVInfo ParseAMVNewsInfo(String Html, int Uin)
        {
            string STR = ReplaceSpecSimbols(Html);
            int startInd = 0;

            // название
            string amvName;
            if (!AMVNews_TryParseName(ref STR, out amvName)) throw new Exception("Не найдено название");
            AMVInfo addAmv = new AMVInfo(Uin, amvName, SITE.AMVNews);

            // теги
            List<string> tags;
            if (!AMVNews_TryParseTags(ref STR, ref startInd, out tags)) throw new Exception("Ошибка нахождения тегов");
            foreach (string tag in tags)
            {
                if (tag.Contains("20")) addAmv.TagOther.Add(tag);
                else addAmv.TagGenre.Add(tag);
            }
            STR = STR.Substring(startInd);
            startInd = 0;

            // коментарий
            if (!AMVNews_TryParseComment(ref STR, ref startInd, out addAmv.Comment)) throw new Exception("Ошибка нахождения комментария");
            STR = STR.Substring(startInd);
            startInd = 0;

            // аниме
            if (!AMVNews_TryParseAnime(ref STR, ref startInd, out addAmv.Anm)) throw new Exception("Не удалось найти аниме");
            STR = STR.Substring(startInd);
            startInd = 0;

            // музыка
            if (!AMVNews_TryParseMusic(ref STR, ref startInd, out addAmv.Msc)) throw new Exception("Не удалось найти музыку");
            STR = STR.Substring(startInd);
            startInd = 0;

            // [награды]
            AMVNews_TryParseAwards(ref STR, ref startInd, out addAmv.Awd);
            STR = STR.Substring(startInd);
            startInd = 0;

            // автор
            if (!AMVNews_TryParseAuthors(ref STR, ref startInd, out addAmv.Atr)) throw new Exception("Не удалось найти автора");
            STR = STR.Substring(startInd);
            startInd = 0;

            // дата создания
            DateTime dtCreate = DateTime.MinValue;
            if (!AMVNews_TryParseCreationDate(ref STR, ref startInd, out dtCreate)) throw new Exception("Не удалось найти дату создания");
            addAmv.Comment = " >> Дата создания: " + dtCreate.ToString("dd.MM.yyyy") + "\r\n" + addAmv.Comment;
            addAmv.TagOther.Add(dtCreate.Year.ToString("0000"));
            addAmv.CreateDate = dtCreate;
            STR = STR.Substring(startInd);
            startInd = 0;

            // [студия]
            List<string> studioList;
            if (AMVNews_TryParseStudio(ref STR, ref startInd, out studioList))
                foreach (string studio in studioList)
                    addAmv.Atr.Add("Студия: " + studio);
            STR = STR.Substring(startInd);
            startInd = 0;

            //дата добавления
            DateTime appenedDate;
            if (!AMVNews_TryParseAppenedDate(ref STR, ref startInd, out appenedDate))
                throw new Exception("Не удалось найти дату добавления");
            addAmv.AppenedDate = appenedDate;
            STR = STR.Substring(startInd);
            startInd = 0;

            // оценка
            decimal rating;
            if (!AMVNews_TryParseRating(ref STR, ref startInd, out rating)) throw new Exception("Не удалось получить оценку");
            addAmv.Rating = rating;

            addAmv.Comment = " >> AMVNews #" + addAmv.Number.ToString() + "\r\n" + addAmv.Comment;
            addAmv.TagOther.Add("AMVNews");

            return addAmv;

        }

        public static AMVInfo ParseAkrossInfo(string Html, int Uin)
        {
            AMVInfo addAmv;

            /*string keyStr = "src=\"http://pagead2.googlesyndication.com/pagead/show_ads.js\">";
             int ind = Html.IndexOf(keyStr);
             string STR = Html.Substring(ind + keyStr.Length, Html.Length - (ind + keyStr.Length));
             STR = ReplaceSpecSimbols(STR);*/
            string STR = ReplaceSpecSimbols(Html);
            int ind;
            string keyStr;
            bool err; // не найдено само ключевое слово
            string rStr;

            #region *** название ***
            keyStr = "<input type='hidden' name='an' value='";
            ind = STR.IndexOf(keyStr);
            if (ind == -1) throw new Exception("Для amv '" + Uin.ToString() + "' Не найдено название");
            else
            {
                int ind1 = STR.IndexOf("'>", ind + keyStr.Length);
                if (ind1 == -1) throw new Exception("Для amv '" + Uin.ToString() + "' Не найдено название");

                string amvName = STR.Substring(ind + keyStr.Length, ind1 - (ind + keyStr.Length)).Trim();
                if (amvName == "") throw new Exception("Для amv '" + Uin.ToString() + "' не найдено название");
                addAmv = new AMVInfoSpace.AMVInfo(Uin, amvName, AMVInfoSpace.SITE.Akross);
            }
            #endregion *** название ***

            #region *** [Оценка] ***
            decimal rating = decimal.MinValue;
            keyStr = "Средняя оценка:</font></td>";
            ind = STR.IndexOf(keyStr);
            if (ind != -1)
            {
                int ind1 = STR.IndexOf("</td>", ind + keyStr.Length);
                if (ind1 == -1) throw new Exception("Для amv '" + Uin.ToString() + "' не удалось определить оценку");
                rStr = STR.Substring(ind + keyStr.Length, ind1 - (ind + keyStr.Length));
                rStr = DelTagsWithInner(rStr);
                if (rStr != "")
                {
                    if (!decimal.TryParse(rStr.Replace('.', ',').Replace("/10", "").Trim(), out rating)) throw new Exception("Для amv '" + Uin.ToString() + "' не удалось определить оценку из строки '" + rStr + "'");
                }

                // всего оценок
                err = false;
                rStr = GetRazdelAkross("Всего оценок:</font>", ref STR, out err);
                if (err) throw new Exception("Для amv '" + Uin.ToString() + "' не удалось определить количество оценок '" + rStr + "'");
                int cntRating;
                if (!int.TryParse(rStr.Replace('.', ',').Trim(), out cntRating)) throw new Exception("Для amv '" + Uin.ToString() + "' не удалось определить оценку из строки '" + rStr + "'");

                if (cntRating < 10) rating = decimal.MinValue;
                else rating /= (decimal)2;

            }

            if (rating != decimal.MinValue) addAmv.Rating = rating;
            #endregion *** [Оценка] ***

            #region *** сокращение html ***
            keyStr = "src=\"http://pagead2.googlesyndication.com/pagead/show_ads.js\">";
            ind = STR.IndexOf(keyStr);
            STR = STR.Substring(ind + keyStr.Length, STR.Length - (ind + keyStr.Length));
            #endregion *** сокращение html ***

            #region *** название (OLD) ***
            /* keyStr = "class=shadow>";
            ind = STR.IndexOf(keyStr);
            if (ind != -1)
            {
                int ind1 = STR.IndexOf("</div>");
                string amvName = STR.Substring(ind + keyStr.Length, ind1 - (ind + keyStr.Length)).Trim();
                if (amvName == "") throw new Exception("Для amv '" + Uin.ToString() + "' не найдено название");
                addAmv = new AmvHtmlParse.AMVInfo(Uin, amvName, AmvHtmlParse.SITE.Akross);
                if (rating != decimal.MinValue) addAmv.Rating = rating;
                STR = STR.Substring(ind1 + 6, STR.Length - (ind1 + 6));
            }
            else throw new Exception("Для amv '" + Uin.ToString() + "' не найдено название");*/
            #endregion *** название ***

            #region *** автор ***
            rStr = GetRazdelAkross("Автор:</font>", ref STR, out err);
            if (rStr != "")
            {
                string[] sl = rStr.Split(',');
                for (int i = 0; i < sl.Length; i++)
                    addAmv.Atr.Add(sl[i].Trim());
            }
            if (rStr == "" || err) throw new Exception("Для amv '" + Uin.ToString() + "' не найден автор");
            #endregion *** автор ***

            #region *** [Студия] ***
            rStr = GetRazdelAkross("Студия:</font>", ref STR, out err);
            if (rStr != "")
            {
                string[] sl = rStr.Split(',');
                foreach (string s in sl)
                    addAmv.Atr.Add("Студия: " + s.Trim());
            }
            #endregion *** [Студия] ***

            #region *** видео ***
            rStr = GetRazdelAkross("Видео:</font>", ref STR, out err);
            if (rStr != "")
            {
                string[] sl = rStr.Split(',');
                for (int i = 0; i < sl.Length; i++)
                    addAmv.Anm.Add(sl[i].Trim());
            }
            if (rStr == "" || err) throw new Exception("Для amv '" + Uin.ToString() + "' не найдено аниме");
            #endregion *** видео ***

            #region *** [Музыка] ***
            rStr = GetRazdelAkross("Музыка:</font>", ref STR, out err);
            if (rStr != "")
            {
                string[] sl = rStr.Split(',');
                for (int i = 0; i < sl.Length; i++)
                    addAmv.Msc.Add(sl[i].Trim());
            }
            /*if (rStr == "" || err) throw new Exception("Для amv '" + Uin.ToString() + "' не найдена музыка");*/
            #endregion *** [Музыка] ***

            #region *** [Жанр (теги)] ***
            rStr = GetRazdelAkross("Жанр:</font>", ref STR, out err);
            if (rStr != "")
            {
                string[] strl = rStr.Split(',');
                foreach (string tagStr in strl)
                {
                    //Теги для замены Akross
                    if (akrossTagReplList == null) akrossTagReplList = new AkrossTagReplList();
                    List<string> sl = AMVInfo.SplitTextForFind(tagStr);
                    bool bGenre = false;
                    foreach (string keyWord in sl)
                    {
                        if (akrossTagReplList.RepList.ContainsKey(keyWord))
                        {
                            foreach (string tag in akrossTagReplList.RepList[keyWord])
                            {
                                if (!addAmv.TagGenre.Contains(tag))
                                    addAmv.TagGenre.Add(tag);
                                bGenre = true;
                            }
                        }
                    }
                    if (!bGenre)
                    {
                        if (sl.Count > 0 && !addAmv.TagOther.Contains(tagStr.Trim()))
                            addAmv.TagOther.Add(tagStr.Trim());
                    }
                }
            }
            if (err) throw new Exception("Для amv '" + Uin.ToString() + "' не найдено ключевое слово Жанр");
            #endregion *** Жанр (теги) ***

            #region *** [Премьера] ***
            rStr = GetRazdelAkross("Премьера:</font>", ref STR, out err);
            if (rStr != "")
            {
                if (rStr != "--")
                {
                    if (rStr == "29.02.2009") rStr = "28.02.2009"; // в одном файле замечено
                    if (rStr == "31.04.2009") rStr = "30.04.2009"; // в одном файле замечено

                    DateTime dt;
                    if (!DateTime.TryParse(rStr, out dt))
                    {
                        // пробуем начти только год
                        int Year;
                        if (!int.TryParse(rStr, out Year))
                        {
                            throw new Exception("Для amv '" + Uin.ToString() + "' не найдена дата премьеры (некорректная дата)");
                        }
                        else
                        {
                            addAmv.Comment = " >> Премьера: " + rStr + "\r\n" + addAmv.Comment;
                            addAmv.TagOther.Add(Year.ToString("0000"));
                        }
                    }
                    else
                    {
                        addAmv.Comment = " >> Премьера: " + rStr + "\r\n" + addAmv.Comment;
                        addAmv.TagOther.Add(dt.Year.ToString("0000"));
                        addAmv.CreateDate = dt;
                    }
                }
            }
            if (err) throw new Exception("Для amv '" + Uin.ToString() + "' не найдено ключевое слово Премьера");
            #endregion *** [Премьера] ***

            #region *** [Дата добавления] ***
            rStr = GetRazdelAkross("Добавлен:</font>", ref STR, out err);
            if (rStr != "")
            {
                if (rStr != "--")
                {
                    if (rStr == "29.02.2009") rStr = "28.02.2009"; // в одном файле замечено
                    if (rStr == "31.04.2009") rStr = "30.04.2009"; // в одном файле замечено

                    DateTime dt;
                    if (!DateTime.TryParse(rStr, out dt))
                    {
                        throw new Exception("Для amv '" + Uin.ToString() + "' не найдена дата добавления (некорректная дата)");
                    }
                    else
                    {
                        addAmv.AppenedDate = dt;
                    }
                }
            }
            if (err) throw new Exception("Для amv '" + Uin.ToString() + "' не найдено ключевое слово Добавлен");
            #endregion *** [Дата добавления] ***

            #region *** [Конкурсы] ***
            rStr = GetRazdelAkross("Конкурсы:</font>", ref STR, out err);
            if (rStr != "")
            {
                addAmv.Awd.Add(rStr);
            }
            if (err) throw new Exception("Для amv '" + Uin.ToString() + "' не найдено ключевое слово Конкурсы");
            #endregion *** [Конкурсы] ***

            #region *** [Награды] ***
            rStr = GetRazdelAkross("Награды:</font>", ref STR, out err);
            if (rStr != "")
            {
                addAmv.Awd.Add(rStr);
            }
            else
            {
                rStr = GetRazdelAkross("<td bgcolor=#FFFFFF align=center><font face=Arial color=green><b>", ref STR, out err);
                if (rStr != "")
                {
                    addAmv.Awd.Add(rStr);
                }
            }

            if (err) throw new Exception("Для amv '" + Uin.ToString() + "' не найдено ключевое слово Награды");
            #endregion *** [Награды] ***

            #region *** [Комментарий автора] ***
            rStr = GetRazdelAkross("Комментарий автора</b></font>", ref STR, out err);
            if (rStr != "")
            {
                addAmv.Comment += "Комментарий:\r\n" + rStr;
            }
            if (err) throw new Exception("Для amv '" + Uin.ToString() + "' не найдено ключевое слово Премьера");
            #endregion *** [Комментарий автора] ***

            addAmv.Comment = " >> Akross #" + addAmv.Number.ToString() + "\r\n" + addAmv.Comment;
            addAmv.TagOther.Add("Akross");
            return addAmv;
        }

        public static AMVInfo ParseCreaConInfo(string Html, int Uin)
        {
            AMVInfo addAmv;

            // замена спец-символов
            string STR = ReplaceSpecSimbols(Html);

            #region *** название ***
            try
            {
                string str = GetSubstring("<h1 class=\"h1megatitle\" itemprop=\"name\">", "</h1>", ref STR);
                addAmv = new AMVInfoSpace.AMVInfo(Uin, str, AMVInfoSpace.SITE.CreaCon);
            }
            catch { throw new Exception("Не найдено название"); }
            #endregion *** название ***

            #region *** [рейтинг] ***
            try
            {
                string str = GetSubstring("<span itemprop=\"ratingValue\">", "</span>", ref STR);
                addAmv.Rating = Convert.ToDecimal(str.Trim().Replace('.', ','));
            }
            catch { /*throw new Exception("Не найден рейтинг");*/ }
            #endregion *** [рейтинг] ***

            #region *** автор ***
            try
            {
                string str = GetSubstring("class=\"dotted_under\">Автор:", "</tr>", ref STR);
                addAmv.Atr.Add(str.Trim());
            }
            catch { throw new Exception("Не найден автор"); }
            #endregion *** автор ***

            #region *** видео ***
            try
            {
                string str = GetSubstring("class=\"dotted_under\">Видеоряд:", "</tr>", ref STR);
                addAmv.Anm.Add(str.Trim());
            }
            catch { throw new Exception("Не найден видеоряд"); }
            #endregion *** видео ***

            #region *** Музыка ***
            try
            {
                string str = GetSubstring("class=\"dotted_under\">Музыка:", "</tr>", ref STR);
                addAmv.Msc.Add(str.Trim());
            }
            catch { throw new Exception("Не найдена музыка"); }
            #endregion *** Музыка ***

            #region *** [Жанр (теги)] ***
            try
            {
                string keyWord = "class=\"dotted_under\">Жанры:";
                int ind = STR.IndexOf(keyWord);
                if (ind != -1)
                {
                    STR = STR.Substring(ind + keyWord.Length);

                    int ind1 = STR.IndexOf("</tr>");
                    if (ind1 == -1) throw new Exception("Найден раздел жанров, но не найдены жанры");
                    string str = STR.Substring(0, ind1);

                    bool bFind = true;
                    ind = 0;
                    while (bFind)
                    {
                        bFind = false;
                        ind1 = str.IndexOf("</span>", ind);
                        if (ind1 == -1) break;

                        string str1 = STR.Substring(ind, ind1 - ind);
                        if (str1.Contains("index.php?music_genre"))
                        {

                            string s = DelTags(str1).Trim();
                            if (s.StartsWith("/ ")) s = s.Substring(2).Trim();
                            if (s != "")
                            {
                                string[] sl = s.Split(',');
                                foreach (string tagMusic in sl)
                                    if (tagMusic.Trim() != "" && !addAmv.TagOther.Contains(tagMusic.Trim()))
                                        addAmv.TagOther.Add(tagMusic.Trim());
                            }

                        }
                        else
                        {
                            string s = DelTags(str1).Trim();
                            if (s.StartsWith("/ ")) s = s.Substring(2).Trim();
                            if (s != "")
                            {
                                if (ReplTagGanreForCreaCon.ContainsKey(s.ToUpper()))
                                    s = ReplTagGanreForCreaCon[s.ToUpper()];
                                addAmv.TagGenre.Add(s);
                            }
                        }

                        ind = ind1 + 7;
                        bFind = true;
                    }

                    /*string str = GetSubstring("class=\"dotted_under\">Жанры:", "</tr>", ref STR);
                    string[] sl = str.Split('/');
                    foreach (string s in sl)
                        addAmv.Tag.Add(s.Trim());*/
                }
            }
            catch { /*throw new Exception("Для файла '" + fl[f] + "' не найдены теги");*/ }
            #endregion *** Жанр (теги) ***

            #region *** [Конкурсы] ***
            //Соревнование:
            try
            {
                string str = GetSubstring("class=\"dotted_under\">Соревнование:", "</tr>", ref STR);
                addAmv.TagOther.Add(str.Trim());
            }
            catch { /*throw new Exception("не найден конкурс");*/ }
            #endregion *** [Конкурсы] ***

            #region *** [Добавлен] ***
            try
            {
                string str = GetSubstring("<td>Добавлен:", "</span>", ref STR);
                addAmv.Comment = "Добавлен: " + str;
                DateTime dt;
                if (DateTime.TryParse(str.Trim(), out dt))
                {
                    addAmv.TagOther.Add(dt.Year.ToString());
                    addAmv.AppenedDate = dt;
                }
            }
            catch { throw new Exception("Не найдена дата добавления"); }
            #endregion *** [Добавлен] ***

            #region *** [Комментарий автора] ***
            try
            {
                string str = GetSubstring("<div class=\"video_descr\" itemprop=\"description\">", "</div>", ref STR);
                addAmv.Comment += "\r\nКомментарий:\r\n" + str;
            }
            catch { /*throw new Exception("Для файла '" + fl[f] + "' не найден комментарий");*/ }
            #endregion *** [Комментарий автора] ***

            addAmv.Comment = " >> CreaCon #" + addAmv.Number.ToString() + "\r\n" + addAmv.Comment;
            addAmv.TagOther.Add("CreaCon");
            return addAmv;
        }

        #region *** для парсинга с AMVNews *****************************************

        /// <summary>Попытка получения имени</summary>
        static bool AMVNews_TryParseNameOld(ref string STR, out string Name)
        {
            Name = "";
            string keyStr = "<title>";
            int ind = STR.IndexOf(keyStr); if (ind == -1) return false;
            int ind1 = STR.IndexOf("</title>"); if (ind1 == -1) return false;

            string[] sl = STR.Substring(ind + keyStr.Length, ind1 - (ind + keyStr.Length)).Split('|');
            string amvName = sl[sl.Length - 1].Trim();
            if (amvName == "") return false;
            Name = amvName;
            return true;
        }

        /// <summary>Попытка получения имени</summary>
        static bool AMVNews_TryParseName(ref string STR, out string Name)
        {
            Name = "";
            string keyStr = "<h1 itemprop=\"name\" class=\"newstitle\">";
            int ind = STR.IndexOf(keyStr); if (ind == -1) return false;
            int ind1 = STR.IndexOf("</h1>"); if (ind1 == -1) return false;
            string amvName = STR.Substring(ind + keyStr.Length, ind1 - (ind + keyStr.Length)).Trim();
            if (amvName == "") return false;
            Name = amvName;
            return true;
        }

        /// <summary>Попытка получения тегов</summary>
        static bool AMVNews_TryParseTags(ref string STR, ref int StartInd, out List<string> Tags)
        {
            Tags = new List<string>();
            int ind = STR.IndexOf("◊", StartInd);
            if (ind == -1) return false;

            StartInd = ind + 1;

            int ind1 = STR.IndexOf("</a> >", StartInd);
            if (ind1 == -1) ind1 = STR.IndexOf("</a>  >", StartInd);
            if (ind1 == -1) return false;

            string tagStr = DelTags(STR.Substring(StartInd, ind1 - StartInd)).Replace("Index", "").Replace("Начало раздела", "").Trim();

            if (tagStr.Length < 2)
            {
                return true;
            }

            if (tagStr[0] == '>') tagStr = tagStr.Substring(1);
            string[] sl = tagStr.Split(',');
            for (int i = 0; i < sl.Length; i++)
            {
                if (sl[i].Trim() != "Оценочная")
                    Tags.Add(sl[i].Trim());
            }
            StartInd = ind1 + 6;
            return true;
        }

        /// <summary>Попытка получения комментария</summary>
        static bool AMVNews_TryParseComment(ref string STR, ref int StartInd, out string Comment)
        {
            Comment = "";
            string[] keyStrList = new string[]
            {
                "<p align=\"justify\">",
                "<P align=justify>",
                "<td colspan=\"2\">"
            };

            foreach (string keyStr in keyStrList)
            {
                int ind = STR.IndexOf(keyStr, StartInd);
                if (ind != -1)
                {
                    StartInd = ind + keyStr.Length;
                    int ind1 = STR.IndexOf("Информация", StartInd);
                    Comment = "Комментарий:\r\n" + DelTags(STR.Substring(StartInd, ind1 - StartInd));
                    StartInd = ind1;
                    return true;
                }
            }

            return false;

        }

        /// <summary>Попытка получения списка аниме</summary>
        static bool AMVNews_TryParseAnime(ref string STR, ref int StartInd, out List<string> AnimeList)
        {
            AnimeList = new List<string>();
            string rStr = "";

            string[] keyStrList = new string[]
            {
                "Аниме<",
                "Аниме:<", 
                "Видео<", 
                "Игра<",
                "Фильм<", 
                "Мультфильм<", 
                "Манга<"
            };
            foreach (string keyStr in keyStrList)
            {
                if (TryGetRazdelAMVNews(keyStr, ref STR, ref StartInd, out rStr))
                    break;
                else rStr = "";
            }

            if (rStr == "") return false;

            string[] sl = rStr.Split(',');
            for (int i = 0; i < sl.Length; i++)
            {
                string[] sl1 = sl[i].Split(';');
                for (int j = 0; j < sl1.Length; j++)
                {
                    if (sl1[j].Trim() != "")
                        AnimeList.Add(sl1[j].Trim());
                }
            }
            return true;
        }

        /// <summary>Попытка получения списка музыки</summary>
        static bool AMVNews_TryParseMusic(ref string STR, ref int StartInd, out List<string> MusicList)
        {
            MusicList = new List<string>();
            string rStr = "";

            string[] keyStrList = new string[] { "Музыка<", "Музыка:<" };

            foreach (string keyStr in keyStrList)
            {
                if (TryGetRazdelAMVNews(keyStr, ref STR, ref StartInd, out rStr))
                    break;
                else rStr = "";
            }
            if (rStr == "") return false;

            string[] sl = rStr.Split(',');
            for (int i = 0; i < sl.Length; i++)
            {
                string[] sl1 = sl[i].Split(';');
                for (int j = 0; j < sl1.Length; j++)
                    MusicList.Add(sl1[j].Trim());
            }
            return true;

        }

        /// <summary>Попытка получения списка наград</summary>
        static bool AMVNews_TryParseAwards(ref string STR, ref int StartInd, out List<string> AwardList)
        {
            AwardList = new List<string>();
            string rStr = "";
            if (!TryGetRazdelAMVNews("Награды<", ref STR, ref StartInd, out rStr)) return false;
            if (rStr != "")
            {
                string[] sl = rStr.Split(',');
                for (int i = 0; i < sl.Length; i++)
                    AwardList.Add(sl[i].Trim());
            }
            return true;
        }

        /// <summary>Попытка получения списка авторов</summary>
        static bool AMVNews_TryParseAuthors(ref string STR, ref int StartInd, out List<string> AuthorList)
        {
            AuthorList = new List<string>();
            int ind = GetIndexOfAny(STR, ref  StartInd, new string[] { "Author: </span>", "Автор: </span>" });
            if (ind == -1) return false;

            int ind1 = STR.IndexOf("</span>", StartInd);
            if (ind1 == -1) throw new Exception("Найден тег автора, но не найдена автор");

            string atr = DelTags(STR.Substring(StartInd, ind1 - StartInd)).Replace("\r\n", "").Trim();
            AuthorList.Add(atr);
            StartInd = ind1 + 7;

            return true;

        }

        /// <summary>Попытка получения даты создания</summary>
        static bool AMVNews_TryParseCreationDate(ref string STR, ref int StartInd, out DateTime CreationDate)
        {
            CreationDate = DateTime.MinValue;
            int ind = GetIndexOfAny(STR, ref  StartInd, new string[] { "(video created: </span>", "(клип создан: </span>" });
            if (ind == -1) return false;

            int ind1 = STR.IndexOf(")</span>", StartInd);
            if (ind1 == -1)
                throw new Exception("Найден тег даты создания, но не найдена дата");
            if (!DateTime.TryParse(DelTags(STR.Substring(StartInd, ind1 - StartInd)).Replace("\r\n", "").Trim(), out CreationDate))
                throw new Exception("Найден тег даты создания, но не найдена дата");
            StartInd = ind1 + 9;
            return true;
        }

        /// <summary>Попытка получения студии</summary>
        static bool AMVNews_TryParseStudio(ref string STR, ref int StartInd, out List<string> StudioList)
        {
            StudioList = new List<string>();
            int ind = GetIndexOfAny(STR, ref  StartInd, new string[] { "Studio:: </span>", "Студия: </span>" });
            if (ind == -1) return false;

            int ind1 = STR.IndexOf("</span>", StartInd);
            if (ind1 == -1) throw new Exception("Найден тег студии, но не найдена студия");
            string s = DelTags(STR.Substring(StartInd, ind1 - StartInd)).Replace("\r\n", "").Trim();
            if (s == "") throw new Exception("Найден тег студии, но не найдена студия");
            StudioList.Add(s);
            StartInd = ind1 + 8;
            return true;
        }

        /// <summary>Попытка получения даты и времени добавления</summary>
        static bool AMVNews_TryParseAppenedDate(ref string STR, ref int StartInd, out DateTime AppenedDate)
        {
            AppenedDate = DateTime.MinValue;
            string keyStr = "Добавил: </span>";
            int ind = STR.IndexOf(keyStr, StartInd);
            if (ind == -1) return false;
            int ind1 = STR.IndexOf("</span>", ind + keyStr.Length);
            if (ind1 == -1) throw new Exception("Найден тег даты добавления, но не найдена дата");
            ind = ind1 + 7;
            ind1 = STR.IndexOf("</span>", ind);
            if (ind1 == -1) throw new Exception("Найден тег даты добавления, но не найдена дата");
            string str = STR.Substring(ind, ind1 - ind).Trim().Replace("в ", "");
            str = DelTags(str) + ":00";
            if (!DateTime.TryParse(str, out AppenedDate)) throw new Exception(string.Format("Дата добавления имеет некорректный формат '{0}'", str));
            return true;
        }

        /// <summary>Попытка получения оценки</summary>
        static bool AMVNews_TryParseRating(ref string STR, ref int StartInd, out decimal Rating)
        {
            Rating = 0;
            string keyStr = "itemprop=\"ratingValue\">";
            int ind = STR.IndexOf(keyStr, StartInd);
            if (ind == -1) return false;
            int ind1 = STR.IndexOf("</span>", ind + keyStr.Length);
            if (ind1 == -1) throw new Exception("Найден тег оценки, но не найдена оценка");
            string str = STR.Substring(ind + keyStr.Length, ind1 - (ind + keyStr.Length)).Trim().Replace('.', ',');
            if (!decimal.TryParse(str, out Rating)) throw new Exception(string.Format("Оценка имеет некорректный формат '{0}'", str));

            return true;
        }

        #endregion *** для парсинга с AMVNews *****************************************

        static string GetRazdelAkross(string keyStr, ref string STR, out bool err)
        {
            int ind = STR.IndexOf(keyStr);
            string ret = "";
            if (ind != -1)
            {
                STR = STR.Substring(ind + keyStr.Length, STR.Length - (ind + keyStr.Length));
                List<int> ind1L = new List<int>();
                ind1L.Add(STR.IndexOf("</b>"));
                ind1L.Add(STR.IndexOf("</font>"));
                int ind1 = int.MaxValue;
                for (int i = 0; i < ind1L.Count; i++) if (ind1L[i] != -1 && ind1 > ind1L[i]) ind1 = ind1L[i];
                if (ind1 == int.MaxValue) { err = true; return ""; }
                string s = STR.Substring(0, ind1);
                //string[] sl = s.Split(':');
                //s = s.Substring(sl[0].Length + 1, s.Length - (sl[0].Length + 1)).Trim();
                ret = DelTags(s);
                if (ret == "--") ret = "";
                STR = STR.Substring(ind1, STR.Length - (ind1));
                err = ret == ""; // раздел найден, но он пуст
            }
            err = false;
            return ret;
        }

        /// <summary>В строке Str находит индекс начала первой же наденой подстроки из перечисленных в Keys, и передвигает StartInd на конец найденной строки</summary>
        static int GetIndexOfAny(string Str, ref int StartInd, string[] Keys)
        {
            int ind = -1;
            foreach (string keyStr in Keys)
            {
                ind = Str.IndexOf(keyStr, StartInd);
                if (ind != -1)
                {
                    StartInd = ind + keyStr.Length;
                    return ind;
                }
            }
            return ind;
        }

        static bool TryGetRazdelAMVNews(string KeyStr, ref string STR, ref int StartInd, out string Result)
        {
            Result = "";
            int ind = STR.IndexOf(KeyStr, StartInd);
            if (ind == -1) return false;

            StartInd = ind + KeyStr.Length;

            // нахождение позиции следующего тега
            string[] ind1_KeyStrList = new string[]
                {
                    "</p>", "</P>",
                    "<br>", "<br />", "<BR>",
                    "</div>", "</STRONG>"
                };
            int ind1 = int.MaxValue;
            foreach (string keyStr in ind1_KeyStrList)
            {
                int ind1_1 = STR.IndexOf(keyStr, StartInd);
                if (ind1_1 != -1 && ind1 > ind1_1)
                    ind1 = ind1_1;
            }
            if (ind1 == int.MaxValue) throw new Exception("Найден тег , но не найдено его окончение"); // если следующий тег не найден, возвращаем ошибку

            // находим строку до следующего тега
            string s = STR.Substring(StartInd, ind1 - StartInd);

            // находим окончания пред-тега
            string[] sl = s.Split(new char[1] { '>' }, 2);
            if (sl.Length == 1) return false; // если окончание пред-тега не найдено, возвращаем ошибку
            s = s.Substring(sl[0].Length + 1, s.Length - (sl[0].Length + 1)).Trim(); // убираем пред-тег спереди
            if (s[0] == ':') s = s.Substring(1, s.Length - 1); // убираем двоеточие спереди, если оно есть
            Result = DelTags(s);
            StartInd = ind1;

            return Result != "";
        }

        static string ReplaceSpecSimbols(string s)
        {
            foreach (KeyValuePair<string, string> val in MnemoHTML)
                if (s.Contains(val.Key)) s = s.Replace(val.Key, val.Value);

            int IND = s.IndexOf("&#");
            while (IND != -1)
            {
                if (IND != -1)
                {
                    int IND1 = s.IndexOf(";", IND + 1);
                    string str = s.Substring(IND + 2, IND1 - IND - 2);

                    Int16 code;
                    if (Int16.TryParse(str, out code))
                    {
                        string str1 = ((char)code).ToString();
                        s = s.Replace("&#" + str + ";", str1);
                        //if (IND != -1) throw new Exception("не найден спец-символ '" + str1 + "' (" + str + ")");
                    }
                }

                IND = s.IndexOf("&#", IND + 2);
            }

            return s;
        }

        /// <summary>Удаление всех тегов из строки</summary>
        static string DelTags(string str)
        {
            StringBuilder ret = new StringBuilder();
            str = str.Replace("<br>", "\r\n").Replace("<BR>", "\r\n").Replace("&nbsp;", " ");
            while (true)
            {
                int ind = str.IndexOf("<");
                if (ind == -1) break;
                int ind1 = str.IndexOf(">");
                if (ind1 == -1)
                    throw new Exception("При удалении тегов, найден открывающий тег и не найден закрывающий\r\n СТРОКА: " + str);

                if (ind != 0) ret.Append(str.Substring(0, ind));
                str = str.Substring(ind1 + 1, str.Length - (ind1 + 1));
            }
            ret.Append(str);
            return ret.ToString().Trim();
        }

        /// <summary>Удаление всех тегов из строки</summary>
        static string DelTagsWithInner(string str)
        {
            StringBuilder ret = new StringBuilder();
            str = str.Replace("<br>", "\r\n").Replace("<BR>", "\r\n").Replace("&nbsp;", " ");
            while (true)
            {
                int ind = str.IndexOf("<");
                if (ind == -1) break;
                int tgCnt = 1;
                int ind1 = -1;
                for (int i = (ind + 1); i < str.Length; i++)
                {
                    if (str[i] == '<') tgCnt++;
                    if (str[i] == '>') tgCnt--;
                    if (tgCnt == 0) { ind1 = i; break; }
                }
                if (ind1 == -1)
                    throw new Exception("При удалении тегов, найден открывающий тег и не найден закрывающий\r\n СТРОКА: " + str);

                if (ind != 0) ret.Append(str.Substring(0, ind));
                str = str.Substring(ind1 + 1, str.Length - (ind1 + 1));
            }
            ret.Append(str);
            return ret.ToString().Trim();
        }

        static Dictionary<string, string> MnemoHTML;
        static AmvHtmlParser()
        {
            MnemoHTML = GetMnemoHTML();
        }

        static Dictionary<string, string> GetMnemoHTML()
        {
            Dictionary<string, string> ret = new Dictionary<string, string>();

            ret.Add("&nbsp;", " ");
            ret.Add("&iexcl;", "¡");
            ret.Add("&cent;", "¢");
            ret.Add("&pound;", "£");
            ret.Add("&curren;", "¤");
            ret.Add("&yen;", "¥");
            ret.Add("&brvbar;", "¦");
            ret.Add("&sect;", "§");
            ret.Add("&uml;", "¨");
            ret.Add("&copy;", "©");
            ret.Add("&ordf;", "ª");
            ret.Add("&laquo;", "«");
            ret.Add("&raquo;", "»");
            ret.Add("&not;", "¬");
            ret.Add("&shy;", "\r\n");
            ret.Add("&reg;", "®");
            ret.Add("&macr;", "¯");
            ret.Add("&deg;", "°");
            ret.Add("&plusmn;", "±");
            ret.Add("&sup2;", "²");
            ret.Add("&sup3;", "³");
            ret.Add("&acute;", "´");
            ret.Add("&micro;", "µ");
            ret.Add("&para;", "¶");
            ret.Add("&middot;", "·");
            ret.Add("&cedil;", "¸");
            ret.Add("&sup1;", "¹");
            ret.Add("&ordm;", "º");
            ret.Add("&frac14;", "¼");
            ret.Add("&frac12;", "½");
            ret.Add("&frac34;", "¾");
            ret.Add("&iquest;", "¿");
            // латинские с доп симполами
            ret.Add("&Agrave;", "À");
            ret.Add("&Aacute;", "Á");
            ret.Add("&Acirc;", "Â");
            ret.Add("&Atilde;", "Ã");
            ret.Add("&Auml;", "Ä");
            ret.Add("&Aring;", "Å");
            ret.Add("&AElig;", "Æ");
            ret.Add("&Ccedil;", "Ç");
            ret.Add("&Egrave;", "È");
            ret.Add("&Eacute;", "É");
            ret.Add("&Ecirc;", "Ê");
            ret.Add("&Euml;", "Ë");
            ret.Add("&Igrave;", "Ì");
            ret.Add("&Iacute;", "Í");
            ret.Add("&Icirc;", "Î");
            ret.Add("&Iuml;", "Ï");
            ret.Add("&ETH;", "Ð");
            ret.Add("&Ntilde;", "Ñ");
            ret.Add("&Ograve;", "Ò");
            ret.Add("&Oacute;", "Ó");
            ret.Add("&Ocirc;", "Ô");
            ret.Add("&Otilde;", "Õ");
            ret.Add("&Ouml;", "Ö");
            ret.Add("&times;", "×");
            ret.Add("&Oslash;", "Ø");
            ret.Add("&Ugrave;", "Ù");
            ret.Add("&Uacute;", "Ú");
            ret.Add("&Ucirc;", "Û");
            ret.Add("&Uuml;", "Ü");
            ret.Add("&Yacute;", "Ý");
            ret.Add("&THORN;", "Þ");
            ret.Add("&szlig;", "ß");
            ret.Add("&agrave;", "à");
            ret.Add("&aacute;", "á");
            ret.Add("&acirc;", "â");
            ret.Add("&atilde;", "ã");
            ret.Add("&auml;", "ä");
            ret.Add("&aring;", "å");
            ret.Add("&aelig;", "æ");
            ret.Add("&ccedil;", "ç");
            ret.Add("&egrave;", "è");
            ret.Add("&eacute;", "é");
            ret.Add("&ecirc;", "ê");
            ret.Add("&euml;", "ë");
            ret.Add("&igrave;", "ì");
            ret.Add("&iacute;", "í");
            ret.Add("&icirc;", "î");
            ret.Add("&iuml;", "ï");
            ret.Add("&eth;", "ð");
            ret.Add("&ntilde;", "ñ");
            ret.Add("&ograve;", "ò");
            ret.Add("&oacute;", "ó");
            ret.Add("&ocirc;", "ô");
            ret.Add("&otilde;", "õ");
            ret.Add("&ouml;", "ö");
            ret.Add("&divide;", "÷");
            ret.Add("&oslash;", "ø");
            ret.Add("&ugrave;", "ù");
            ret.Add("&uacute;", "ú");
            ret.Add("&ucirc;", "û");
            ret.Add("&uuml;", "ü");
            ret.Add("&yacute;", "ý");
            ret.Add("&thorn;", "þ");
            ret.Add("&yuml;", "ÿ");

            //Ссылки-мнемоники для символов, математических символов и греческих букв
            ret.Add("&fnof", "ƒ");
            ret.Add("&Alpha;", "Α");
            ret.Add("&Beta;", "Β");
            ret.Add("&Gamma;", "Γ");
            ret.Add("&Delta;", "Δ");
            ret.Add("&Epsilon;", "Ε");
            ret.Add("&Zeta;", "Ζ");
            ret.Add("&Eta;", "Η");
            ret.Add("&Theta;", "Θ");
            ret.Add("&Iota;", "Ι");
            ret.Add("&Kappa;", "Κ");
            ret.Add("&Lambda;", "Λ");
            ret.Add("&Mu;", "Μ");
            ret.Add("&Nu;", "Ν");
            ret.Add("&Xi;", "Ξ");
            ret.Add("&Omicron;", "Ο");
            ret.Add("&Pi;", "Π");
            ret.Add("&Rho;", "Ρ");
            ret.Add("&Sigma;", "Σ");
            ret.Add("&Tau;", "Τ");
            ret.Add("&Upsilon;", "Υ");
            ret.Add("&Phi;", "Φ");
            ret.Add("&Chi;", "Χ");
            ret.Add("&Psi;", "Ψ");
            ret.Add("&Omega;", "Ω");
            ret.Add("&alpha;", "α");
            ret.Add("&beta;", "β");
            ret.Add("&gamma;", "γ");
            ret.Add("&delta;", "δ");
            ret.Add("&epsilon;", "ε");
            ret.Add("&zeta;", "ζ");
            ret.Add("&eta;", "η");
            ret.Add("&theta;", "θ");
            ret.Add("&iota;", "ι");
            ret.Add("&kappa;", "κ");
            ret.Add("&lambda;", "λ");
            ret.Add("&mu;", "μ");
            ret.Add("&nu;", "ν");
            ret.Add("&xi;", "ξ");
            ret.Add("&omicron;", "ο");
            ret.Add("&pi;", "π");
            ret.Add("&rho;", "ρ");
            ret.Add("&sigmaf;", "ς");
            ret.Add("&sigma;", "σ");
            ret.Add("&tau;", "τ");
            ret.Add("&upsilon;", "υ");
            ret.Add("&phi;", "φ");
            ret.Add("&chi;", "χ");
            ret.Add("&psi;", "ψ");
            ret.Add("&omega;", "ω");
            ret.Add("&thetasy;", "ϑ");
            ret.Add("&upsih;", "ϒ");
            ret.Add("&piv;", "ϖ");
            //Основная пунктуация
            ret.Add("&bull;", "•");
            ret.Add("&hellip;", "…");
            ret.Add("&prime;", "′");
            ret.Add("&Prime;", "″");
            ret.Add("&oline;", "‾");
            ret.Add("&frasl;", "⁄");
            //Буквоподобные символы
            ret.Add("&weierp;", "℘");
            ret.Add("&image;", "ℑ");
            ret.Add("&real;", "ℜ");
            ret.Add("&trade;", "™");
            ret.Add("&alefsym;", "ℵ");
            //Стрелки
            ret.Add("&larr;", "←");
            ret.Add("&uarr;", "↑");
            ret.Add("&rarr;", "→");
            ret.Add("&darr;", "↓");
            ret.Add("&harr;", "↔");
            ret.Add("&crarr;", "↵");
            ret.Add("&lArr;", "⇐");
            ret.Add("&uArr;", "⇑");
            ret.Add("&rArr;", "⇒");
            ret.Add("&dArr;", "⇓");
            ret.Add("&hArr;", "⇔");
            //Математические операции
            ret.Add("&forall;", "∀");
            ret.Add("&part;", "∂");
            ret.Add("&exist;", "∃");
            ret.Add("&empty;", "∅");
            ret.Add("&nabla;", "∇");
            ret.Add("&isin;", "∈");
            ret.Add("&notin;", "∉");
            ret.Add("&ni;", "∋");
            ret.Add("&prod;", "∏");
            ret.Add("&sum;", "∑");
            ret.Add("&minus;", "−");
            ret.Add("&lowast;", "∗");
            ret.Add("&radic;", "√");
            ret.Add("&prop;", "∝");
            ret.Add("&infin;", "∞");
            ret.Add("&ang;", "∠");
            ret.Add("&and;", "∧");
            ret.Add("&or;", "∨");
            ret.Add("&cap;", "∩");
            ret.Add("&cup;", "∪");
            ret.Add("&int;", "∫");
            ret.Add("&there4;", "∴");
            ret.Add("&sim;", "∼");
            ret.Add("&cong;", "≅");
            ret.Add("&asymp;", "≈");
            ret.Add("&ne;", "≠");
            ret.Add("&equiv;", "≡");
            ret.Add("&le;", "≤");
            ret.Add("&ge;", "≥");
            ret.Add("&sub;", "⊂");
            ret.Add("&sup;", "⊃");
            ret.Add("&nsub;", "⊄");
            ret.Add("&sube;", "⊆");
            ret.Add("&supe;", "⊇");
            ret.Add("&oplus;", "⊕");
            ret.Add("&otimes;", "⊗");
            ret.Add("&perp;", "⊥");
            ret.Add("&sdot;", "⋅");
            //Различные Технические
            ret.Add("&lceil;", "⌈");
            ret.Add("&rceil;", "⌉");
            ret.Add("&lfloor;", "⌊");
            ret.Add("&rfloor;", "⌋");
            ret.Add("&lang;", "〈");
            ret.Add("&rang;", "〉");
            //Геометрические Фигуры
            ret.Add("&loz;", "◊");
            //Различные Символы
            ret.Add("&spades;", "♠");
            ret.Add("&clubs;", "♣");
            ret.Add("&hearts;", "♥");
            ret.Add("&diams;", "♦");

            //Ссылки-мнемоники для символов разметки и интернационализации

            //C0 Controls and Basic Latin
            ret.Add("&apos;", "'");
            ret.Add("&quot;", "\"");
            ret.Add("&amp;", "&");
            ret.Add("&lt;", "<");
            ret.Add("&gt;", ">");
            //Latin Extended-A
            ret.Add("&OElig;", "Œ");
            ret.Add("&oelig;", "œ");
            ret.Add("&Scaron;", "Š");
            ret.Add("&scaron;", "š");
            ret.Add("&Yuml;", "Ÿ");
            //Модификаторы букв
            ret.Add("&circ;", "ˆ");
            ret.Add("&tilde;", "˜");
            //Общая Пунктуация
            ret.Add("&ensp;", " ");
            ret.Add("&emsp;", " ");
            ret.Add("&thinsp;", " ");
            ret.Add("&zwnj;", "");
            ret.Add("&zwj;", "");
            ret.Add("&lrm;", "");
            ret.Add("&rlm;", "");
            ret.Add("&ndash;", "–");
            ret.Add("&mdash;", "—");
            ret.Add("&lsquo;", "‘");
            ret.Add("&rsquo;", "’");
            ret.Add("&sbquo;", "‚");
            ret.Add("&ldquo;", "“");
            ret.Add("&rdquo;", "”");
            ret.Add("&bdquo;", "„");
            ret.Add("&dagger;", "†");
            ret.Add("&Dagger;", "‡");
            ret.Add("&permil;", "‰");
            ret.Add("&lsaquo;", "‹");
            ret.Add("&rsaquo;", "›");
            ret.Add("&euro;", "€");

            return ret;
        }

        /// <summary> Возвращает текст между ключевыми строками StartStr и EndStr
        /// строка STR укорачивается до EndStr включительно (то есть найденая EndStr тоже удаляется из STR)
        /// </summary>
        /// <param name="StartStr">строка для поиска старта</param>
        /// <param name="EndStr">строка для поиска окончания</param>
        /// <param name="STR">общий текст</param>
        public static string GetSubstring(string StartStr, string EndStr, ref string STR)
        {
            string ret = "";

            // нахождение начала
            int indSt = STR.IndexOf(StartStr);
            if (indSt == -1) throw new Exception("Не найдено стартовое ключевое слово");
            indSt += StartStr.Length;

            // нахождение окончания
            int indEn = STR.IndexOf(EndStr, indSt);
            if (indEn == -1) throw new Exception("Не найдено ключевое слово окончания");

            // находим строку 
            ret = STR.Substring(indSt, indEn - indSt);
            // удаляем теги
            ret = DelTags(ret);
            // сокращаем основной текст
            STR = STR.Substring(indEn + EndStr.Length);

            return ret;
        }

        #endregion *** Parse AmvInfo ********************************************************************

    }

}
